<?php
class Template{
	private static $_instance;//类实例
	protected $_options = array();//设置项
	private   $vars     = array();//变量
	private   $leftTag  = '{[';//模板左标签
	private   $rightTag = ']}';//模板右标签
	/**
	 * @access private
	 * 构造函数 私有 禁止直接 new Template 形式实例化
	 * 使用 Template::getInstance() 实例化
	 */
	protected function __construct(){}
	/**
	 * @access public 
	 * @static
	 * 实例化本类
	 * 单例模式
	 */
	public static function getInstance(){
		empty(self::$_instance) && self::$_instance=new Template();
		return self::$_instance;
	}
	/**
	 * 显示模板
	 * @param string $path 模板路径
	 */
	public function display($path){
		if (substr($path, 0,3)==='_F_'){
			$dir=FW_SYS_TPL;
			$path=substr($path, 3);
		}
		else{
			$dir=FW_APP_TPL;
		}
		$dir.=$path;
		$dir.='.html';
		$this->loadTpl($this->run($dir));
	}
	private function loadTpl($dir){
		extract($this->vars);
		include $dir;
	}
	/**
	 * @access private
	 * 运行模板引擎
	 * @param string $path 模板路径
	 * @return string 编译后模板路径
	 */
	private function run($path){
		if ($this->cache($path)){
			return $this->cacheFileName($path);
		}
		else{
			file_put_contents($this->cacheFileName($path), $this->parseInner($path));
			return $this->cacheFileName($path); 
		}
	}
	private function parseInner($path){
		$inner=file_get_contents($path);
		$inner=preg_replace('/('.preg_quote($this->leftTag).')([\\d\\D]*?)('.preg_quote($this->rightTag).')/eis', "\$this->parse('\\2')",$inner);
		$inner="<?php \r\ndefined ('FW') or exit('Access Denide.');\r\n/* FWPHP 模板缓存文件 缓存时间:".date('Y年m月d日 H时i分s秒').". */ \r\n?>\r\n".$inner;
		return $inner;
	}
	/**
	 * 正则处理接口
	 * @param string $string 处理字符串
	 * @return string 处理后PHP代码
	 */
	public function parse($string){
		if (substr ($string,0,1)==='$'){
			return $this->parseVar($string);
		}
		elseif (substr ($string,0,7)==='foreach'){
			return $this->parseForeach(substr($string,7));
		}
		elseif (substr ($string,0,10)==='endforeach'){
			return $this->parseEndForeach();
		}
		elseif (substr ($string,0,2)==='if'){
			return $this->parseIf(substr($string, 2));
		}
		elseif (substr ($string,0,6)==='elseif'){
			return $this->parseElseIf(substr($string, 6));
		}
		elseif (substr ($string,0,5)==='endif'){
			return $this->parseEndIf();
		}
		elseif (substr ($string,0,4)==='else'){
			return $this->parseElse();
		}
		elseif (substr ($string,0,2)==='E:'){
			return $this->parseEcho(substr($string,2));
		}
		elseif (substr($string,0,7)==='include'){
			return $this->includeTpl(substr($string,8));
		}
		elseif (substr($string,0,2)==='C:'){
			return $this->parseConstant(substr($string,2));
		}
		elseif (substr ($string,0,1)===':'){
			return $this->parseEcho(substr($string,1));
		}
		elseif (substr($string,0,2)==='T:'){
			//return $this->parseEcho(substr($string,2));
		}
		elseif (substr($string,0,2)==='//'){
			return '';
		}
		FW::E('模板引擎错误 未知模板语句 ['.$string.']');
		return $string;
	}
	public function parseVar($name){
		if (strpos($name, '|')!==FALSE){
			$array=explode ('|',$name);
			return '<?php echo '.$this->_parseFunc($array[1],$this->_getVar($array[0])).'; ?>';
		}
		$code=$this->_getVar($name);
		$code='<?php echo '.$code.'; ?>';
		return $code;
	}
	public function parseConstant($name){
		return constant($name);
	}
	private function _parseFunc($func,$var=NULL){
		preg_match('#(.*?)(?:\((.*)\))#i', $func,$result);
		$funcName=$result[1];
		$args=$result[2];
		$args=str_replace('###', $var, $args);
		$code=$funcName.'('.$args.')';
		return $code;
	}
	private function _getVar($name){
		$name=explode ('.',$name);//以.符号表示数组下标或对象
		//[debug]var_dump($name);
		if($name[0]==='$_S'){
			$var_list=array('_GET','_POST','_COOKIE','_FILE','_ENV','_REQUEST','_SERVER','_SESSION');
			array_shift($name);
			$var_name=array_shift ($name);
			if (in_array($var_name,$var_list)){
				$code='$'.$var_name;
				foreach ($name as $value){
					$code.='[\''.FW::quotes($value).'\']';
				}
				return $code;
			}
		}
		$tmp=array_shift ($name);//变量名
		$code=$tmp;
		if (is_array($name) && !empty ($name)){
			foreach ($name as $value){
				if ($value{0}==='$'){
					$code.='['.$value.']';
				}
				else{
					$code.='[\''.FW::quotes($value).'\']';
				}
			}				
		}
		return $code;
	}
	private function parseForeach($string){
		$string=trim ($string);
		$count=substr_count($string, '$');
		$array=explode (' ',$string);
		$code='if (is_array('.FW::quotes($array[0]).')):';
		
		switch ($count){
			case 3:
				$code.='foreach ('.FW::quotes($array[0]).' as '.FW::quotes($array[1]).'=> '.FW::quotes($array[2]).'):';
			break;
			case 2:
				$code.='foreach ('.FW::quotes($array[0]).' as '.FW::quotes($array[1]).'):';
			break;
			default:
				FW::E ('模板引擎错误 [foreach] 标签 格式不正确 ['.$string.']');
			break;
		}
		return '<?php '.$code.' ?>';
	}
	private function parseEndForeach(){
		return '<?php endforeach;endif; ?>';
	}
	private function parseIf($name){
		$name=trim($name);
		return '<?php if '.$name.' : ?>';
	}
	private function parseElseIf ($name){
		$name=trim($name);
		return '<?php elseif '.$name.' : ?>';
	}
	private function parseElse (){
		return '<?php else: ?>';
	}
	private function parseEndIf (){
		return '<?php endif; ?>';
	}
	private function parseEcho ($string){
		return '<?php echo '.$string.'; ?>';
	}
	private function includeTpl ($path){
		ob_start();
		$this->display($path);
		return ob_get_clean();
	}
	private function checkAorO(&$var){
		if (is_array($var)){
			return true;
		}
		return false;
	}
	/**
	 * 模板缓存检查
	 * @param string $path 模板路径
	 * @return bool 缓存是否有效
	 */
	private function cache($path){
		if (FW_APP_DEBUG){
			return false;
		}
		$cacheTime=FW::C('CACHE_TIME');
		if ($cacheTime==0){
			return FALSE;
		}
		if (!is_file($this->cacheFileName($path))){
			return false;
		}
		$tplTime=filemtime($path);
		$cacheTime=filemtime($this->cacheFileName($path));
		if ($tplTime>$cacheTime || time()-$cacheTime>$cacheTime){
			return false;
		}
		else{
			return true;
		}
	}
	/**
	 * @access private
	 * 获取缓存文件名
	 * 
	 * @param string $path 模板路径
	 * @return 
	 */
	private function cacheFileName($path){
		$name=md5_file($path);
		$name=basename($path,'.html').'_'.substr($name,8,16);
		$stirng=FW_APP_RUNTIME.'Tpl_c/'.$name.'.html';
		return $stirng;
	}
	public function __get($name){
		return isset($this->_options[$name])?$this->_options[$name]:NULL;
	}
	public function __set($name,$value){
		$this->_options[$name]=$value;
	}
	public function assign($name,$value=NULL){
		if (is_array($name)){
			$this->vars=array_merge($this->vars,$name);
			return;
		}
		elseif ($value===NULL){
			return isset($this->vars[$name])?$this->vars[$name]:NULL;
		}
		else{
			$this->vars[$name]=$value;
			return;
		}
	}
}